#pragma once

#include <iostream>
#include <vector>

namespace command {
    
/// 命令模式 + 组合模式 = 宏命令
/// 宏命令包含了一组命令，它充当了具体命令与调用者的双重角色，执行它时将递归调用它所包含的所有命令

/// @brief 抽象命令类, 声明执行命令的接口
class AbstractMacroCommand {
public:
    virtual ~AbstractMacroCommand() {}
    virtual void Execute() = 0;
};

/// @brief 命令接收者, 执行命令功能的相关操作，是具体命令对象业务的真正实现者
class CompositeReceiver {
public:
    void ActionA() {
        printf("this is CompositeReceiver::ActionA!\n");
    }
    void ActionB() {
        printf("this is CompositeReceiver::ActionB!\n");
    }
};

/// @brief 具体命令类, 它拥有接收者对象，并通过调用接收者的功能来完成命令要执行的操作
class ConcreteCommandA : public AbstractMacroCommand {
public:
    ConcreteCommandA() {
        receiver_.reset(new CompositeReceiver());
    }
    virtual void Execute() {
        receiver_->ActionA();
    }

private:
    std::shared_ptr<CompositeReceiver> receiver_;
};
class ConcreteCommandB : public AbstractMacroCommand {
public:
    ConcreteCommandB() {
        receiver_.reset(new CompositeReceiver());
    }
    virtual void Execute() {
        receiver_->ActionB();
    }

private:
    std::shared_ptr<CompositeReceiver> receiver_;
};

/// @brief 命令请求者, 请求的发送者，它通常拥有很多的命令对象，并通过访问命令对象来执行相关请求，它不直接访问接收者
class CompositeInvoker{
public:
    void AddCommand(AbstractMacroCommand* cmd) {
        cmds_.emplace_back(cmd);
    }
    void RemoveCommand(AbstractMacroCommand* cmd) {
        for (auto it = cmds_.begin(); it != cmds_.end(); it++) {
            if (cmd == (*it)) {
                cmds_.erase(it++);
                break;
            }
        }
    }
    void Request() {
        for (auto* cmd : cmds_) {
            cmd->Execute();
        }
    }

private:
    std::vector<AbstractMacroCommand*> cmds_;
};

} // namespace command

void Test_15_command_impl_2() {
    printf("-------------------- %s --------------------\n", __FUNCTION__);
    std::shared_ptr<command::AbstractMacroCommand> cmd_A(new command::ConcreteCommandA());
    std::shared_ptr<command::AbstractMacroCommand> cmd_B(new command::ConcreteCommandB());

    command::CompositeInvoker client;
    client.AddCommand(cmd_A.get());
    client.AddCommand(cmd_B.get());
    
    client.Request();
}

// -------------------- Test_15_command_impl_2 --------------------
// this is CompositeReceiver::ActionA!
// this is CompositeReceiver::ActionB!